from functools import partial

from kivy.graphics import Color, Rectangle, Line
from kivy.input import MotionEvent
from kivy.properties import StringProperty, NumericProperty, ListProperty, ObjectProperty
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.checkbox import CheckBox
from kivy.uix.dropdown import DropDown
from kivy.uix.label import Label

"""
eg1:
Button:
  id: ground_ids
  text: "请选择门票场次"
  text_size: self.size
  halign: 'left'
  valign: 'middle'
  dropdown: ground_ids_dropdown.__self__  # 保证对下拉框的引用，防止被gc回收
  on_parent: ground_ids_dropdown.dismiss()  # 设置下拉框默认隐藏
  on_release: ground_ids_dropdown.open(self)  # 设置点击显示下拉框
MultiSelectDropDown:
  id: ground_ids_dropdown
  main_button: ground_ids  # 和下拉框绑定的按钮，用于显示和交互
  options: [ '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15' ] # 下拉选项
  selected_options: ['1','3']  # 默认选中选项
  placeholder: '请选择门票场次'  # 下拉框一项都没选中时显示到main_button的值，未设置此项时显示main_button最初的text值
  separator: ','  # 每项之间的分隔符，默认为","
  left_width: 50  # Checkbox占用的宽度
  item_height: 40 # 每个下拉选项的高度；每个下拉选项的宽度默认和main_button相同，可以通过width属性修改
  color:  (1, 0, 0, 1) # 下拉框文字颜色
  # 需要获取下拉框选中项时：ground_ids.text 可以获取选中文本；ground_ids_dropdown.selected_options 可以获取选中项的列表
  # 其他属性与DropDown相同
  
eg2:
.kv file
# 点击时间绑定下拉框
Button:
  size_hint: (None, None)
  size: (200, 50)
  id: dropdown2_text
  text: "请选择门票场次2"
  on_release: root.dropdown2.open(self)

.python file  
# 给root节点声明一个dropdown2属性
options = ['场次一', '场次二', '场次三', '场次四']
dropdown2 = MultiSelectDropDown(main_button=root.ids.dropdown2_text, options=options, selected_options=['场次二', '场次三'])
root.dropdown2 = dropdown2
"""


class MultiSelectDropDown(DropDown):
    """自定义下拉复选框"""
    # 配置DropDown默认属性
    color = ListProperty([0, 0, 0, 1])  # 下拉项的文字颜色

    # 自定义属性
    options = ListProperty([])    # 下拉数据
    selected_options = ListProperty([])  # 独立的选中项状态
    main_button = ObjectProperty()  # 与下拉框绑定的按钮
    placeholder = StringProperty(None)  # 未选择数据时
    separator = StringProperty(',')  # 分隔符
    left_width = NumericProperty(40)  # checkbox的宽度
    item_height = NumericProperty(30)  # 下拉项的高度
    option_border_color = ListProperty([.3, .9, .3, .5])  # 下拉项边框颜色
    option_background_color = ListProperty([.9, .2, .4, .5])  # 下拉项背景色

    def __init__(self, **kwargs):
        super(MultiSelectDropDown, self).__init__(**kwargs)
        if kwargs.get('options'):
            self.options = kwargs.get('options')  # 下拉数据
        if 'selected_options' in kwargs:
            self.selected_options = kwargs.get('selected_options')  # 独立的选中项状态
        if kwargs.get('main_button'):
            self.main_button = kwargs.get('main_button')  # 与下拉框绑定的按钮
        if kwargs.get('placeholder'):
            self.placeholder = kwargs.get('placeholder')  # 未选择数据时
        if kwargs.get('separator'):
            self.separator = kwargs.get('separator')  # 未选择数据时
        if kwargs.get('left_width'):
            self.left_width = kwargs.get('left_width')  # checkbox的宽度
        if kwargs.get('item_height'):
            self.item_height = kwargs.get('item_height')  # 下拉项的高度
        if kwargs.get('color'):
            self.color = kwargs.get('color')  # 下拉项的文字颜色
        self.bind(options=self.options_change_event)
        self.bind(selected_options=self.selected_options_change_event)

    def on_kv_post(self, base_widget):
        self.reload_widgets()  # 当从python文件中调用时，需要kv文件加载完以后触发一次加载

    def select_widgets(self, options=None):
        """
        根据提供的选项列表更新复选框的状态
        :param options: str 或 可迭代类型
        :return:
        """
        if isinstance(options, str):
            options = [options]
        self.selected_options = list(set(self.selected_options) | set(options))  # 将options合并到selected_options中
        self.refresh_selected_widgets()

    def options_change_event(self, dropdown, options):
        """
        options变化时触发，重新加载所有下拉项
        :param dropdown:
        :param options: 变化后的options
        :return:
        """
        self.reload_widgets()

    def selected_options_change_event(self, dropdown, selected_options):
        """
        selected_options变化时触发，刷新需选中的下拉项
        :param dropdown:
        :param selected_options: 变化后的selected_options
        :return:
        """
        """根据selected_options刷新下拉框勾选状态"""
        for child in self.children[0].children:
            checkbox = child.children[1]  # 复选框在标签后面
            label = child.children[0]  # 通过标签文本找到复选框
            checkbox.active = label.text in self.selected_options
        self.update_button_text()

    def reload_widgets(self):
        """
        重新加载下拉框的所有选项
        :return:
        """
        super(MultiSelectDropDown, self).clear_widgets()
        for option in self.options:
            self.add_widget(option)
        self.update_button_text()

    def add_widgets(self, options=None, **kwargs):
        """
        批量添加下拉节点方法（将不存在于self.options的值拼接到当前下拉项中）
        """
        if options:
            for option in options:
                if option not in self.options:
                    self.options.append(option)
                    self.add_widget(option)
            self.update_button_text()

    def add_widget(self, option, **kwargs):
        """添加一个下拉选项"""
        checkbox = CheckBox(size_hint=(None, None), size=(self.left_width, self.item_height))
        label = Label(text=option, size_hint=(None, None), size=(100, self.item_height), color=self.color)
        label.text_size = label.size
        label.halign = 'left'
        label.valign = 'middle'

        # 是否默认选中
        checkbox.active = option in self.selected_options
        # 设置复选框状态，使用 partial 捕获当前选项
        checkbox.bind(active=partial(self.on_checkbox_active, option))

        # 将按钮和复选框放入一个水平布局
        layout = BoxLayout(size_hint_y=None, height=self.item_height)

        # 设置背景色
        with layout.canvas.before:
            Color(*self.option_background_color)
            layout.rect = Rectangle(pos=layout.pos, size=layout.size)

        # 设置边框
        with layout.canvas:
            Color(*self.option_border_color)
            layout.line = Line(rectangle=(layout.x, layout.y, layout.width, layout.height), width=1)

        # 更新矩形和边框的大小和位置
        layout.bind(size=self.update_rect, pos=self.update_rect)

        layout.add_widget(checkbox)
        layout.add_widget(label)

        # 为整个layout添加点击事件
        layout.bind(on_touch_down=partial(self.on_layout_click, checkbox))

        super(MultiSelectDropDown, self).add_widget(layout, **kwargs)  # 调用父类的方法

    def update_rect(self, layout, *args):
        layout.rect.pos = layout.pos
        layout.rect.size = layout.size
        layout.line.rectangle = (layout.x, layout.y, layout.width, layout.height)

    def on_layout_click(self, checkbox, layout, touch: MotionEvent):
        # 检查触摸位置
        if layout.collide_point(*touch.pos):
            if not checkbox.collide_point(*touch.pos):  # 如果不是点击在 CheckBox 区域
                checkbox.active = not checkbox.active  # 切换复选框状态

    def on_checkbox_active(self, option, checkbox, value):
        """复选框的选中事件"""
        if value:
            if option not in self.selected_options:
                self.selected_options.append(option)
        else:
            if option in self.selected_options:
                self.selected_options.remove(option)
        # 更新按钮的文本，保持顺序
        self.update_button_text()

    def update_button_text(self):
        if not self.placeholder:
            self.placeholder = self.main_button.text
        # 根据原始 options 的顺序生成 selected_options 的文本
        ordered_selected = [opt for opt in self.options if opt in self.selected_options]
        self.main_button.text = self.separator.join(ordered_selected) or self.placeholder

    def update_label_width(self, label, checkbox):
        # 计算剩余宽度并设置标签宽度
        remaining_width = label.parent.width - checkbox.width - 10  # 减去复选框宽度和间距
        label.width = max(remaining_width, 0)  # 设置标签宽度





